/*
 * Copyright (c) 2011, Marc-Andre Ferland - madrang@gmail.com
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the The Warrent Team nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *     * This software is free for non-commercial use. You may not use this
 *       software, in whole or in part, in support of any commercial product
 *       without the express consent of the author.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE WARRENT TEAM BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
using System;
using Gdk;
using Gtk;
using LibNotify = Notifications;

namespace OsHelper.Notify.Linux
{
	internal class LibNotifyHelper
	{
		const int StatusIconSize = 24;
		const int NoteIconSize = 48;
		
		const int LettersPerWord = 7;
		const int MillisecondsPerWord = 350;
		const int MinNotifyShow = 5000;
		const int MaxNotifyShow = 10000;
		
		
		private static Pixbuf pxDefaultIcon;
		public static Pixbuf DefaultIcon
		{
			get
			{
				if(pxDefaultIcon == null)
					pxDefaultIcon = DrawingService.LoadIcon (OsHelper.Notify.Notification.DefaultIconName,
					                                         NoteIconSize);
				
				return pxDefaultIcon;
			}
		}	
		static StatusIcon siStatusIcon;
		static StatusIcon statusIcon
		{
			get
			{
				if(siStatusIcon == null) {
					siStatusIcon = new StatusIcon ();
					siStatusIcon.Pixbuf = DrawingService.LoadIcon (OsHelper.Notify.Notification.DefaultIconName,
					                                               StatusIconSize);
					siStatusIcon.Visible = false;
				}
				return siStatusIcon;
			}
		}
		
		private static bool IsNotifyOSD ()
		{
			return LibNotify.Global.ServerInformation.Name == "notify-osd";
		}
		
		private static int ReadableDurationForMessage (string title, string message)
		{
			int t = (title.Length + message.Length) / LettersPerWord * MillisecondsPerWord;	
			return Math.Min (Math.Max (t, MinNotifyShow), MaxNotifyShow);
		}
		
		private enum NotificationCapability {
			actions,
			append,
			body,
			body_hyperlinks,
			body_images,
			body_markup,
			icon_multi,
			icon_static,
			image_svg,
			max,
			positioning, // not an official capability
			scaling, // not an official capability
			sound
		}
		
		private static bool SupportsCapability (NotificationCapability capability)
		{
			// positioning and scaling are not actual capabilities, i just know for a fact most other servers
			// support geo. hints, and notify-osd is the only that auto scales images
			if (capability == NotificationCapability.positioning)
				return !IsNotifyOSD ();
			else if (capability == NotificationCapability.scaling)
				return IsNotifyOSD ();
			
			return Array.IndexOf (LibNotify.Global.Capabilities, Enum.GetName (typeof (NotificationCapability), capability)) > -1;
		}
		
		
		public event EventHandler<NotificationEventArgs> NotificationClosed;
		
		Pixbuf Icon { get; set; }
		
		public LibNotifyHelper ()
		{
		}
		
		private void OnNotificationClosed (Notification note)
		{
			if (NotificationClosed != null)
				NotificationClosed (this, new NotificationEventArgs (note));
		}



		/*public void Notify (Notification note)
		{
			this.Notify (note, Screen.Default, 0, 0);
		}
		
		public void Notify (Notification note, Screen screen, int x, int y)
		{
			OsHelper.DebugTools.Log.Debug<LibNotifyHelper> ("Screen: {0}, X: {1}, Y: {2}", (screen == null ? "Null": "Ok"), x, y);
			
			LibNotify.Notification notify = ToNotify (note);
			notify.SetGeometryHints (screen, x, y);
			notify.Show ();
		}*/

		private LibNotify.Notification ToNotify (Notification note)
		{
			LibNotify.Notification notify = new LibNotify.Notification ();
			notify.Body = GLib.Markup.EscapeText (note.Message);
			notify.Summary = GLib.Markup.EscapeText (note.Title);
			notify.Closed += (sender, e) => OnNotificationClosed (note);
			notify.Timeout = ReadableDurationForMessage (note.Title, note.Message);
			
			if (SupportsCapability (NotificationCapability.scaling) && !note.Icon.Contains ("@")) {
				notify.IconName = string.IsNullOrEmpty (note.Icon) ? OsHelper.Notify.Notification.DefaultIconName : note.Icon;
			} else notify.Icon = string.IsNullOrEmpty (note.Icon) ? DefaultIcon : DrawingService.LoadIcon (note.Icon, NoteIconSize);

			/*if (note is ActionableNotification) {
				ActionableNotification anote = note as ActionableNotification;
				notify.AddAction (GLib.Markup.EscapeText (anote.ActionLabel),
				    anote.ActionLabel, (sender, e) => anote.PerformAction ());
			}*/

			return notify;
		}

		public static LibNotify.Notification Notify (string title, string message, string icon)
		{
			//If user dont use gtk we need to init.
			Gtk.Application.Init();
			
			if (IsNotifyOSD ()) {
				return Notify (title, message, icon, Screen.Default, 0, 0);
			}
			
			Screen screen = Screen.Default;
			int x = 0, y = 0;
			
			// if we aren't using notify-osd, show a status icon
			Work.RunOnGtkThread (() => {
				statusIcon.Visible = true;
			});
			
			Rectangle area;
			Orientation orientation;

			statusIcon.GetGeometry (out screen, out area, out orientation);
			x = area.X + area.Width / 2;
			y = area.Y + area.Height - 5;
			
			return Notify (title, message, icon, screen, x, y);
		}
		
		static LibNotify.Notification Notify (string title, string message, string icon, Screen screen, int x, int y)
		{
			LibNotify.Notification notify = ToNotify (title, message, icon);
			
			OsHelper.DebugTools.Logger.Default.Debug<LibNotifyHelper> ("Default Screen: {0}", (Screen.Default == null ? "Null": "Ok"));
			OsHelper.DebugTools.Logger.Default.Debug<LibNotifyHelper> ("Screen: {0}, X: {1}, Y: {2}", (screen == null ? "Null": "Ok"), x, y);
			
			notify.SetGeometryHints (screen, x, y);
			notify.Show ();
			
			notify.Closed += delegate {
				Work.RunOnGtkThread (() => statusIcon.Visible = false );
			};
			
			return notify;
		}
		
		static LibNotify.Notification ToNotify (string title, string message, string icon)
		{
			LibNotify.Notification notify = new LibNotify.Notification ();
			notify.Body = GLib.Markup.EscapeText (message);
			notify.Summary = title;
			notify.Timeout = ReadableDurationForMessage (title, message);
			
			if (LibNotifyHelper.SupportsCapability (LibNotifyHelper.NotificationCapability.scaling) && !icon.Contains ("@")) {
				notify.IconName = string.IsNullOrEmpty (icon) ? OsHelper.Notify.Notification.DefaultIconName : icon;
			} else notify.Icon = string.IsNullOrEmpty (icon) ? DefaultIcon : DrawingService.LoadIcon (icon, NoteIconSize);

			return notify;
		}
	}
	
}